package pl.shenlon.poker.game.gametypes;

import pl.shenlon.poker.game.PlayerAction;
import pl.shenlon.poker.game.PlayerActionType;
import pl.shenlon.poker.game.PokerPlayer;

public class NoLimitPokerGame extends PokerGame {

	{
		betSize = smallBlind;
	}

	public NoLimitPokerGame() {
		super(5);
	}

	public NoLimitPokerGame(PokerPlayer playerA, PokerPlayer playerB) {
		this();
		this.playerA = playerA;
		this.playerB = playerB;
	}

	@Override
	public int nextBetAmount() {
		return 2 * betSize;
	}

	private void performTurn(int... cardsToShow) {
		raiseCounter = 0;
		tableCards.show(cardsToShow);
		PlayerAction playerAAction = playerA.act(this, PlayerActionType.CHECK,
				PlayerActionType.CALL, PlayerActionType.BLUFF);
		PlayerActionType actionType = playerAAction.getType();
		boolean bidHasBeenRaised = false;
		if (actionType.equals(PlayerActionType.BLUFF)
				|| actionType.equals(PlayerActionType.CALL)) {
			pot += betSize;
			playerA.addCash(-betSize);
			bidHasBeenRaised = playerBBid();
		} else {
			bidHasBeenRaised = playerBBid();
			bidHasBeenRaised = playerABid(bidHasBeenRaised) || bidHasBeenRaised;
		}

		if (bidHasBeenRaised) {
			raiseCounter++;
		}

		if (winner != null) {
			return;
		}

		while (raiseCounter < MAX_RAISE_COUNT) {
			bidHasBeenRaised = playerBBid();
			if (winner != null) {
				return;
			}
			bidHasBeenRaised = playerABid(bidHasBeenRaised) || bidHasBeenRaised;
			if (winner != null || !bidHasBeenRaised) {
				return;
			}
			raiseCounter++;
		}
	}


	private boolean playerABid(boolean playerBRaised) {
		PlayerAction playerAAction = null;

		if (!playerBRaised) {
			if (playerA.canBet(nextBetAmount())) {
				playerAAction = playerA.act(this, PlayerActionType.RAISE,
						PlayerActionType.CALL, PlayerActionType.FOLD);
			} else if (playerA.canBet(betSize)) {
				playerAAction = playerA.act(this, PlayerActionType.CALL,
						PlayerActionType.FOLD);
			} else {
				playerAAction = playerA.act(this, PlayerActionType.FOLD);
			}
		} else {
			if (playerA.canBet(nextBetAmount())) {
				playerAAction = playerA.act(this, PlayerActionType.BLUFF,
						PlayerActionType.FOLD, PlayerActionType.CALL,
						PlayerActionType.RAISE);
			} else if (playerA.canBet(betSize)) {
				playerAAction = playerA.act(this, PlayerActionType.BLUFF,
						PlayerActionType.FOLD, PlayerActionType.CALL);
			} else {
				playerAAction = playerA.act(this, PlayerActionType.FOLD);
			}
		}

		PlayerActionType actionType = playerAAction.getType();
		setChanged();
		notifyObservers("A " + actionType);
		if (actionType.equals(PlayerActionType.FOLD)) {
			winner = playerB;
			return false;
		} else if (actionType.equals(PlayerActionType.BLUFF)
				|| actionType.equals(PlayerActionType.CALL)) {
			pot += betSize;
			playerA.addCash(-betSize);
			playerATotalBets += betSize;
		} else {
			betSize *= 2;
			pot += betSize;
			playerA.addCash(-betSize);
			playerATotalBets += betSize;
			return true;
		}
		return false;
	}

	private boolean playerBBid() {
		PlayerAction playerBAction = null;
		if (playerB.canBet(nextBetAmount())) {
			playerBAction = playerB.act(this, PlayerActionType.BLUFF,
					PlayerActionType.FOLD, PlayerActionType.CALL,
					PlayerActionType.RAISE);
		} else if (playerB.canBet(betSize)) {
			playerBAction = playerB.act(this, PlayerActionType.BLUFF,
					PlayerActionType.FOLD, PlayerActionType.CALL);
		} else {
			playerBAction = playerB.act(this, PlayerActionType.FOLD);
		}

		PlayerActionType actionType = playerBAction.getType();
		setChanged();
		notifyObservers("B " + actionType);
		if (actionType.equals(PlayerActionType.FOLD)) {
			winner = playerA;
			return false;
		} else if (actionType.equals(PlayerActionType.BLUFF)
				|| actionType.equals(PlayerActionType.CALL)) {
			pot += betSize;
			playerB.addCash(-betSize);
			playerBTotalBets += betSize;
			return false;
		} else {
			betSize *= 2;
			pot += betSize;
			playerB.addCash(-betSize);
			playerBTotalBets += betSize;
			return true;
		}
	}

	@Override
	public void turnFlop() {
		performTurn(0, 1, 2);
	}

	@Override
	public void turnPreFlop() {
		raiseCounter = 0;
		while (raiseCounter < MAX_RAISE_COUNT) {
			boolean bidHasBeenRaised = playerBBid();
			if (winner != null) {
				return;
			}
			bidHasBeenRaised = playerABid(bidHasBeenRaised) || bidHasBeenRaised;
			if (winner != null || !bidHasBeenRaised) {
				return;
			}
			raiseCounter++;
		}

	}

	@Override
	public void turnRiver() {
		performTurn(4);
	}

	@Override
	public void turnTurn() {
		performTurn(3);
	}

}
